package services

import (
	"encoding/json"
	"errors"
	"gitee.com/phpdi/ant-api/entitys"
	"gitee.com/phpdi/ant-api/utils"
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/cache"
	"github.com/astaxie/beego/logs"
	"github.com/astaxie/beego/orm"
	"reflect"
	"time"
	_ "github.com/go-sql-driver/mysql"

)

type CacheClosure = func() interface{}

var (
	CacheService cache.Cache
)

//服务初始化
func init() {
	var err error
	if err = InitLog(); err != nil {
		logs.Warn("日志初始化:", err)
	}

	if err = InitMysql(); err != nil {
		logs.Warn("mysql初始化:", err)
	}

	if err = InitCache(); err != nil {
		logs.Warn("缓存初始化:", err)
	}

	if err = InitVar(); err != nil {
		logs.Warn("全局变量初始化:", err)
	}
}

//初始化mysql
func InitMysql() error {
	//读取配置文件，设置数据库参数

	dbPrefix := beego.AppConfig.String("mysql::prefix")
	//数据库名称
	dbName := beego.AppConfig.String("mysql::dbname")
	//数据库连接用户名
	dbUser := beego.AppConfig.String("mysql::user")
	//数据库连接用户名
	dbPwd := beego.AppConfig.String("mysql::pwd")
	//数据库IP（域名）
	dbHost := beego.AppConfig.String("mysql::host")

	logs.Info("连接数据库:",dbHost)
	//数据库端口
	dbPort := beego.AppConfig.String("mysql" + "::port")
	dbCharset := beego.AppConfig.DefaultString("mysql"+"::charset", "utf8")

	if err := orm.RegisterDataBase("default", "mysql", dbUser+":"+dbPwd+"@tcp("+dbHost+":"+dbPort+")/"+dbName+"?charset="+dbCharset, 30); err != nil {
		return err
	}

	orm.RegisterModelWithPrefix(dbPrefix,
		new(entitys.User),
		new(entitys.Role),
		new(entitys.Perm),
		new(entitys.RolePerm),
		new(entitys.RoleUser),
	)

	orm.SetMaxIdleConns("default", 1000)
	orm.SetMaxOpenConns("default", 1000)

	db, err := orm.GetDB("default")
	if err != nil {
		beego.Warn("初始化数据库错误：", err)
	}

	//设置链接存活时间，防止数据重启后句柄失效
	db.SetConnMaxLifetime(30)

	return nil
}

//开发模式
func IsDev() bool {
	return beego.AppConfig.String("runmode") == beego.DEV
}

//线上环境
func IsProd() bool {
	return beego.AppConfig.String("runmode") ==  beego.PROD
}

//初始化缓存服务
func InitCache() (err error) {
	CacheService,err=cache.NewCache("memory", `{"interval":60}`)
	return
}

//初始化日志
func InitLog() error {

	var (
		channelLens int64
		err         error
	)

	if IsProd() {
		channelLens = 10000
	} else {
		channelLens = 1
	}

	log := logs.NewLogger(channelLens)

	if IsProd() {
		err = log.SetLogger(logs.AdapterMultiFile, `{"filename":"logs/rms.log",
		"separate":["emergency", "alert", "critical", "error", "warning", "notice", "info", "debug"],
		"level":`+beego.AppConfig.String("logs::level")+`,
		"daily":true,
		"maxdays":10}`)
	} else {
		err = log.SetLogger(logs.AdapterConsole)
	}

	if err != nil {
		return err
	}

	//异步记录
	log.Async()

	return nil
}

//系统的所有全局变量初始化的地方
func InitVar() error {
	entitys.JwtSecretKey="JwtSecretKey"

	return nil
}

// TableName 下面是统一的表名管理
func TableName(name string) string {
	prefix := beego.AppConfig.String( "mysql::prefix")
	return prefix + name
}

//beego orm
func Orm() orm.Ormer {
	return orm.NewOrm()
}

//数据重复
func Repeat(obj interface{}, table string, pk string, fields ...string) (error, bool) {

	objT := reflect.TypeOf(obj)
	if !utils.IsStructPtr(objT) {
		return errors.New("obj 必须为结构体指针"), true
	}

	objV := reflect.ValueOf(obj).Elem()

	q := Orm().QueryTable(table)
	for _, field := range fields {
		q = q.Filter(field, objV.FieldByName(field).Interface())
	}

	pkVal := objV.FieldByName(pk).Interface()
	if !reflect.DeepEqual(pkVal, 0) {
		q = q.Exclude(pk, pkVal)
	}

	err := q.One(obj)

	if err != nil {
		//没有重复的数据
		return nil, false
	}

	return nil, true
}

//获取和存储缓存 timeout 单位秒
func RememberCache(key string, value interface{}, timeout int, closure CacheClosure) error {
	var (
		resByte []byte
		res     interface{}
		err     error
	)

	if value == nil {
		return nil
	}

	if v := reflect.TypeOf(value); v.Kind() != reflect.Ptr {
		return errors.New("value 必须为指针")
	}

	res = CacheService.Get(key)

	if res == nil {
		closureRes := closure()
		if resByte, err = json.Marshal(closureRes); err != nil {
			return err
		}

		if err := CacheService.Put(key, resByte, time.Duration(timeout)*time.Second); err != nil {
			return err
		}

	}else{
		resByte=res.([]byte)
	}

	return json.Unmarshal(resByte, value)
}


